package com.cms.plugin.db;

import java.lang.reflect.Method;
import java.util.Map;
import java.util.Map.Entry;

import javax.servlet.http.HttpServletRequest;

import com.cms.common.exception.ActiveRecordException;
import com.cms.common.kit.StrKit;

public final class ModelInjector {

	@SuppressWarnings("unchecked")
	public static <T> T inject(Class<?> modelClass, HttpServletRequest request,
			boolean skipConvertError) {
		String modelName = modelClass.getSimpleName();
		return (T) inject(modelClass, StrKit.firstCharToLowerCase(modelName),
				request, skipConvertError);
	}
	
	@SuppressWarnings("unchecked")
	public static <T> T inject(Class<?> modelClass, Map<String,Object> params,
			boolean skipConvertError) {
		String modelName = modelClass.getSimpleName();
		return (T) inject(modelClass, StrKit.firstCharToLowerCase(modelName),
				params, skipConvertError);
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	public static final <T> T inject(Class<?> modelClass, String modelName,
			HttpServletRequest request, boolean skipConvertError) {
		Object model = null;
		try {
			model = modelClass.newInstance();
		} catch (Exception e) {
			throw new RuntimeException(e);
		}

		if (model instanceof Model)
			injectActiveRecordModel((Model) model, modelName, request,
					skipConvertError);
		else
			injectCommonModel(model, modelName, request, modelClass,
					skipConvertError);

		return (T) model;
	}
	
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public static final <T> T inject(Class<?> modelClass, String modelName,
			Map<String,Object> params, boolean skipConvertError) {
		Object model = null;
		try {
			model = modelClass.newInstance();
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
		injectActiveRecordModel((Model) model, modelName, params,skipConvertError);
		return (T) model;
	}
	
	private static final void injectCommonModel(Object model, String modelName,
			HttpServletRequest request, Class<?> modelClass,
			boolean skipConvertError) {
		Method[] methods = modelClass.getMethods();
		for (Method method : methods) {
			String methodName = method.getName();
			if (methodName.startsWith("set") == false) // only setter method
				continue;

			Class<?>[] types = method.getParameterTypes();
			if (types.length != 1) // only one parameter
				continue;

			String attrName = methodName.substring(3);
			String value = request.getParameter(modelName + "."
					+ StrKit.firstCharToLowerCase(attrName));
			if (value != null) {
				try {
					method.invoke(model, TypeConverter.convert(types[0], value));
				} catch (Exception e) {
					if (skipConvertError == false)
						throw new RuntimeException(e);
				}
			}
		}
	}

	@SuppressWarnings("rawtypes")
	private static final void injectActiveRecordModel(Model<?> model,
			String modelName, HttpServletRequest request,
			boolean skipConvertError) {
		Table table = TableMapping.me().getTable(model.getClass());
		String modelNameAndDot = modelName + ".";
		Map<String, String[]> parasMap = request.getParameterMap();
		for (Entry<String, String[]> e : parasMap.entrySet()) {
			    String paraKey = e.getKey();
				String paraName = paraKey;
				Class colType = table.getColumnType(paraName);
				if (colType == null)
					throw new ActiveRecordException("The model attribute "
							+ paraKey + " is not exists.");
				String[] paraValue = e.getValue();
				try {
					// Object value = Converter.convert(colType, paraValue !=
					// null ? paraValue[0] : null);
					Object value = paraValue[0] != null ? TypeConverter
							.convert(colType, paraValue[0]) : null;
					model.set(paraName, value);
				} catch (Exception ex) {
					if (skipConvertError == false)
						throw new RuntimeException(
								"Can not convert parameter: " + modelNameAndDot
										+ paraName, ex);
				}
		}
	}

	@SuppressWarnings("rawtypes")
	private static final void injectActiveRecordModel(Model<?> model,
			String modelName, Map<String, Object> parasMap,
			boolean skipConvertError) {
		Table table = TableMapping.me().getTable(model.getClass());

		for (Entry<String, Object> e : parasMap.entrySet()) {
			String paraName = e.getKey();
			Class colType = table.getColumnType(paraName);
			if (colType == null)
				throw new ActiveRecordException("The model attribute "
						+ paraName + " is not exists.");
			String paraValue = (String) e.getValue();
			try {
				// Object value = Converter.convert(colType, paraValue != null ?
				// paraValue[0] : null);
				Object value = paraValue != null ? TypeConverter.convert(
						colType, paraValue) : null;
				model.set(paraName, value);
			} catch (Exception ex) {
				if (skipConvertError == false)
					throw new RuntimeException("Can not convert parameter: "
							+ paraName, ex);
			}
		}
	}

}